/**
* Copyright 2016 Bryan Kelly
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
*
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.github.btkelly.gandalf;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.gson.JsonDeserializer;
import io.github.btkelly.gandalf.checker.DefaultHistoryChecker;
import io.github.btkelly.gandalf.checker.DefaultVersionChecker;
import io.github.btkelly.gandalf.checker.GateKeeper;
import io.github.btkelly.gandalf.checker.HistoryChecker;
import io.github.btkelly.gandalf.models.Alert;
import io.github.btkelly.gandalf.models.Bootstrap;
import io.github.btkelly.gandalf.models.OptionalUpdate;
import io.github.btkelly.gandalf.network.BootstrapApi;
import io.github.btkelly.gandalf.network.BootstrapCallback;
import io.github.btkelly.gandalf.holders.DialogStringsHolder;
import io.github.btkelly.gandalf.utils.LoggerUtil;
import io.github.btkelly.gandalf.utils.LoggerUtil.LogLevel;
import io.github.btkelly.gandalf.utils.OnUpdateSelectedListener;
import io.github.btkelly.gandalf.utils.PlayStoreUpdateListener;
import io.github.btkelly.gandalf.utils.StringUtils;
import okhttp3.OkHttpClient;
/**
* This is the public api that allows consumers to configure the Gandalf library.
*/
public final class Gandalf {
private static Gandalf gandalfInstance;
private final Context context;
private final OkHttpClient okHttpClient;
private final String bootstrapUrl;
private final HistoryChecker historyChecker;
private final GateKeeper gateKeeper;
private final OnUpdateSelectedListener onUpdateSelectedListener;
private final JsonDeserializer<Bootstrap> customDeserializer;
private final DialogStringsHolder dialogStringsHolder;
private Gandalf(Context context, OkHttpClient okHttpClient, String bootstrapUrl, HistoryChecker historyChecker, GateKeeper gateKeeper,
OnUpdateSelectedListener onUpdateSelectedListener, JsonDeserializer<Bootstrap> customDeserializer,
DialogStringsHolder dialogStringsHolder) {
this.context = context;
this.okHttpClient = okHttpClient;
this.bootstrapUrl = bootstrapUrl;
this.historyChecker = historyChecker;
this.gateKeeper = gateKeeper;
this.onUpdateSelectedListener = onUpdateSelectedListener;
this.customDeserializer = customDeserializer;
this.dialogStringsHolder = dialogStringsHolder;
}
public static Gandalf get() {
synchronized (Gandalf.class) {
if (gandalfInstance == null) {
throw new IllegalStateException("You must first install a version of the Gandalf class using the Installer class");
}
}
return gandalfInstance;
}
private static Gandalf createInstance(@NonNull final Context context,
@NonNull final OkHttpClient okHttpClient,
@NonNull final String bootstrapUrl,
@NonNull final HistoryChecker historyChecker,
@NonNull final GateKeeper gateKeeper,
@NonNull final OnUpdateSelectedListener onUpdateSelectedListener,
@Nullable final JsonDeserializer<Bootstrap> customDeserializer,
@NonNull final DialogStringsHolder dialogStringsHolder) {
return new Gandalf(context,
okHttpClient,
bootstrapUrl,
historyChecker,
gateKeeper,
onUpdateSelectedListener,
customDeserializer,
dialogStringsHolder);
}
/**
* Returns the OnUpdateSelectedListener set during the Gandalf install
* @return the onUpdateSelectedListener set during install
*/
public OnUpdateSelectedListener getOnUpdateSelectedListener() {
return this.onUpdateSelectedListener;
}
/**
* Returns the DialogStringsHolder instance set during the Gandalf Install
* @return the DialogStringsHolder instance set during install
*/
public DialogStringsHolder getDialogStringsHolder() {
return dialogStringsHolder;
}
/**
* Saves the provided {@link OptionalUpdate} to the history checker.
* @param optionalUpdate the provided current optional update information
* @return {@code true} if {@code optionalUpdate} was successfully saved
*/
public boolean save(@NonNull final OptionalUpdate optionalUpdate) {
return this.historyChecker.save(optionalUpdate);
}
/**
* Saves the provided {@link Alert} to the history checker.
* @param alert this should be the current alert
* @return {@code true} if {@code alert} was successfully saved
*/
public boolean save(@NonNull final Alert alert) {
return this.historyChecker.save(alert);
}
/**
* Starts the flow on checking a remote file and determining if any updates or alerts are available.
* @param gandalfCallback - a callback interface to respond to events from the bootstrap check
*/
public void shallIPass(final GandalfCallback gandalfCallback) {
LoggerUtil.logD("Fetching bootstrap");
new BootstrapApi(context, okHttpClient, bootstrapUrl, customDeserializer)
.fetchBootstrap(new BootstrapCallback() {
@Override
public void onSuccess(Bootstrap bootstrap) {
LoggerUtil.logD("Fetched bootstrap: " + bootstrap);
if (gateKeeper.updateIsRequired(bootstrap)) {
LoggerUtil.logD("Update is required");
gandalfCallback.onRequiredUpdate(bootstrap.getRequiredUpdate());
} else if (gateKeeper.showAlert(bootstrap)) {
LoggerUtil.logD("Alert");
gandalfCallback.onAlert(bootstrap.getAlert());
} else if (gateKeeper.updateIsOptional(bootstrap)) {
LoggerUtil.logD("Update is optional");
gandalfCallback.onOptionalUpdate(bootstrap.getOptionalUpdate());
} else {
LoggerUtil.logD("No action is required");
gandalfCallback.onNoActionRequired();
}
}
@Override
public void onError(Exception e) {
LoggerUtil.logE("Error fetching bootstrap: " + e.getMessage());
//In any error case we should let the user in as to not block based on a bug
gandalfCallback.onNoActionRequired();
}
});
}
/**
* Basically a builder that can be used once to `install` the singleton Gandalf instance used by the library
*/
public static class Installer {
private Context context;
private OkHttpClient okHttpClient;
private String bootstrapUrl;
private String packageName;
private OnUpdateSelectedListener onUpdateSelectedListener;
private JsonDeserializer<Bootstrap> customDeserializer;
@LogLevel private int logLevel = LoggerUtil.NONE;
private DialogStringsHolder dialogStringsHolder;
public Installer setContext(Context context) {
this.context = context;
return this;
}
public Installer setOkHttpClient(OkHttpClient okHttpClient) {
this.okHttpClient = okHttpClient;
return this;
}
public Installer setBootstrapUrl(@NonNull String bootstrapUrl) {
this.bootstrapUrl = bootstrapUrl;
return this;
}
public Installer setPackageName(String packageName) {
this.packageName = packageName;
return this;
}
public Installer setOnUpdateSelectedListener(OnUpdateSelectedListener onUpdateSelectedListener) {
this.onUpdateSelectedListener = onUpdateSelectedListener;
return this;
}
public Installer setCustomDeserializer(JsonDeserializer<Bootstrap> customDeserializer) {
this.customDeserializer = customDeserializer;
return this;
}
public Installer setLogLevel(@LogLevel final int logLevel) {
this.logLevel = logLevel;
return this;
}
public Installer setDialogStringsHolder(DialogStringsHolder dialogStringsHolder) {
this.dialogStringsHolder = dialogStringsHolder;
return this;
}
public void install() {
synchronized (Gandalf.class) {
if (gandalfInstance != null) {
throw new IllegalStateException("Install can only be called once and should be called inside your application onCreate()");
}
if (this.context == null) {
throw new IllegalStateException("You must supply a valid context");
}
if (StringUtils.isBlank(this.bootstrapUrl)) {
throw new IllegalStateException("You must supply a bootstrap url");
}
if (StringUtils.isBlank(this.packageName) && this.onUpdateSelectedListener == null) {
throw new IllegalStateException("You must supply a package name or a custom OnUpdateSelectedListener");
}
if (this.dialogStringsHolder == null) {
this.dialogStringsHolder = new DialogStringsHolder(context);
}
if (this.onUpdateSelectedListener == null) {
this.onUpdateSelectedListener = new PlayStoreUpdateListener(this.packageName);
}
HistoryChecker historyChecker = new DefaultHistoryChecker(this.context);
LoggerUtil.setLogLevel(logLevel);
gandalfInstance = createInstance(
this.context,
this.okHttpClient,
this.bootstrapUrl,
historyChecker,
new GateKeeper(this.context, new DefaultVersionChecker(), historyChecker),
this.onUpdateSelectedListener,
this.customDeserializer,
this.dialogStringsHolder
);
}
}
}
}